import xml.etree.ElementTree as ET
import re
from itertools import product
from typing import NamedTuple, List
from openpyxl import Workbook
from collections import defaultdict

def parse_freemind_to_BDD_case(file_path: str):
    tree = ET.parse(file_path)
    root = tree.getroot()[0]
    if root is None:
        return

    print(f'测试用例需求:{root.attrib['TEXT']}')

    params = {}
    params_root_node = next((child for child in root if child.attrib['TEXT'] == 'Params'), [])
    for param in params_root_node:
        param_name = param.attrib['TEXT']
        param_optional_value = [child.attrib['TEXT'] for child in param]
        params[param_name] = param_optional_value
    print(f'测试用例参数情况:{params}')

    param_pattern = re.compile(r'\$\{([^}]+)\}')

    cases = []
    node_value_path = []
    def dfs(node: ET.Element):
        cur_node_value = node.attrib['TEXT']
        match_params = param_pattern.findall(cur_node_value)

        def travel_in_children(root):
            for child in root:
                if len(child) != 0:
                    dfs(child)
                else:
                    cases.append(node_value_path[:] + [child.attrib['TEXT']])
        
        if len(match_params) == 0:
            node_value_path.append(cur_node_value)
            travel_in_children(node)
            node_value_path.pop()
            return  

        replace_choice_list = product(*[params[param_name] for param_name in match_params])
        for choice in replace_choice_list:
            tmp = cur_node_value[:]
            for i, param_name in enumerate(match_params):
                tmp = tmp.replace('${' + param_name + '}', choice[i])
            node_value_path.append(tmp[:])
            travel_in_children(node)
            node_value_path.pop()
    
    for child in root:
        if child.attrib['TEXT'] != 'Params':
            dfs(child)

    return cases

class PingCodeCase(NamedTuple):
    title: str
    module: str = ''
    case_id: str = ''
    maintainer: str = '杜天赐'
    case_type: str = '功能测试'
    criticality: str = 'P0'
    test_type: str = '手动'
    estimated_man_hours: float = 0.0
    left_man_hours: float = 0.0
    relevance_job: str = ''
    given: str = ''
    steps: str = ''
    results: str = ''
    follower: str = '杜天赐'
    remark: str = ''


def generate_pingcode_cases_excel(cases: List[List[str]], save_path = 'PingCodeCase.xlsx'):
    pingcode_cases = []
    scenario_2_cases = defaultdict(list)
    for index, case_data in enumerate(cases):
        steps = []
        results = []
        givens = []
        scenario = ''
        last_is_step = False
        for value in case_data:
            if value.startswith('When'):
                steps.append(value)
                if last_is_step:
                    results.append('')
                    last_is_step = False
                last_is_step = True
                continue
            
            if value.startswith('Then') or value.startswith('And'):
                if not last_is_step:
                    steps.append('')
                results.append(value)
            elif value.startswith('Given'):
                givens.append(value)
            elif value.startswith('Scenario'):
                scenario = value
            
            last_is_step = False
        scenario_2_cases[scenario].append((givens, steps, results))


    for scenario, case_bdd_data in scenario_2_cases.items():
        if len(case_bdd_data) == 0:
            continue
            
        steps = results = ''
        for index, case in enumerate(case_bdd_data):
            givens, case_steps, case_results = case
            sub_title = f'{index + 1}.Sub_Case_{index + 1}\n' + '\n'.join(givens)
            steps += sub_title + '\n'.join([f'→{i+1}.{step}' for i, step in enumerate(case_steps)]) + '\n'
            results += sub_title + '\n'.join([f'→{i+1}.{result}' for i, result in enumerate(case_results)]) + '\n'
            
        pingcode_cases.append(
            PingCodeCase(
                title=scenario,
                given='前置条件在每个SubCase的Given给出',
                steps=steps,
                results=results
        ))


    book = Workbook()
    sheet = book.active

    #pingcode的识别格式
    sheet.append([])
    sheet.title = 'test_case'
    tmp = '模块|编号|*标题|维护人|用例类型|重要程度|测试类型|预估工时|剩余工时|关联工作项|前置条件|步骤描述|预期结果|关注人|备注'
    heads = tmp.split('|')
    sheet.append(heads)

    for case_data in pingcode_cases:
        sheet.append([
            case_data.module, case_data.case_id, case_data.title, case_data.maintainer, case_data.case_type, case_data.criticality,
            case_data.test_type, case_data.estimated_man_hours, case_data.left_man_hours, case_data.relevance_job, 
            case_data.given, case_data.steps, case_data.results, case_data.follower, case_data.remark
        ])
    
    book.save(save_path)



cases = parse_freemind_to_BDD_case(r'c:\Users\Deatr\Downloads\Feature_【业务需求】标准数据看板与导出.mm')
generate_pingcode_cases_excel(cases)
print(cases) 